/**
 * 
 */
package qy.jalgotrade.dataseries.resampled;

import java.time.ZonedDateTime;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import qy.jalgotrade.bar.Bar;
import qy.jalgotrade.bar.Bar.Frequency;
import qy.jalgotrade.dataseries.DataSeries;
import qy.jalgotrade.dataseries.SequenceDataSeries;
import qy.jalgotrade.event.EventHandler;
import qy.jalgotrade.utils.resamplebase.Grouper;
import qy.jalgotrade.utils.resamplebase.TimeRange;

/**
 * @author c-geo
 *
 */
public class ResampledDataSeries<T> extends SequenceDataSeries<T> implements DSResampler<T> {

	/**
	 * @author c-geo
	 *
	 */
	private class DSResamplerImpl implements DSResampler<T>, EventHandler {

		private Frequency __frequency;

		private Grouper<T> __grouper;

		private TimeRange __range;

		/**
		 * 
		 */
		public DSResamplerImpl() {

		}

		/**
		 * 
		 * @param dataSeries
		 * @param frequency
		 */
		public void initDSResampler(DataSeries<T> dataSeries, Frequency frequency) {

			__frequency = frequency;
			__grouper = null;
			__range = null;

			((SequenceDataSeries<?>) dataSeries).getNewValueEvent().subscribe(this);
		}

		/*
		 * 
		 */
		@Override
		public void handle(String eventName, Map<String, Object> params) throws Exception {

			DataSeries<?> dataSeries = (DataSeries<?>) params.get("dataSeries");
			ZonedDateTime dateTime = (ZonedDateTime) params.get("dateTime");
			@SuppressWarnings("unchecked")
			T value = (T) params.get("value");
			__onNewValue(dataSeries, dateTime, value);
		}

		/**
		 * 
		 * @param dataSeries
		 * @param dateTime
		 * @param value
		 * @throws Exception
		 */
		private void __onNewValue(DataSeries<?> dataSeries, ZonedDateTime dateTime, T value) throws Exception {

			if (__range == null) {

				__range = TimeRange.buildRange(dateTime, __frequency);
				__grouper = buildGrouper(__range, value, __frequency);

			} else if (__range.belongs(dateTime)) {

				__grouper.addValue(value);

			} else {

				ResampledDataSeries.this.appendWithDateTime(__grouper.getDateTime(), __grouper.getGrouped());
				__range = TimeRange.buildRange(dateTime, __frequency);
				__grouper = buildGrouper(__range, value, __frequency);
			}
		}

		/*
		 * 
		 */
		@Override
		public Grouper<T> buildGrouper(TimeRange range, T value, Frequency frequency) {

			return new AggFunGrouper<>(range.getBeginning(), ResampledDataSeries.this.__aggfun);
		}

		/*
		 * 
		 */
		@Override
		public void pushLast() throws Exception {

			if (__grouper != null) {
				ResampledDataSeries.this.appendWithDateTime(__grouper.getDateTime(), __grouper.getGrouped());
				__grouper = null;
				__range = null;
			}
		}

		/*
		 * 
		 */
		@Override
		public void checkNow(ZonedDateTime dateTime) throws Exception {

			if (__range != null && !!__range.belongs(dateTime)) {
				ResampledDataSeries.this.appendWithDateTime(__grouper.getDateTime(), __grouper.getGrouped());
				__grouper = null;
				__range = null;
			}
		}
	}

	private DSResamplerImpl __dsResamplerImpl;

	private Function<List<T>, T> __aggfun;

	/**
	 * 
	 * @param dataSeries
	 * @param frequency
	 * @param aggfun
	 * @throws Exception
	 */
	public ResampledDataSeries(DataSeries<T> dataSeries, Frequency frequency, Function<List<T>, T> aggfun)
			throws Exception {

		this(dataSeries, frequency, aggfun, 0, Object.class);
	}

	/**
	 * 
	 * @param dataSeries
	 * @param frequency
	 * @param aggfun
	 * @param maxLen
	 * @throws Exception
	 */
	public ResampledDataSeries(DataSeries<T> dataSeries, Frequency frequency, Function<List<T>, T> aggfun,
			int maxLen) throws Exception {

		this(dataSeries, frequency, aggfun, maxLen, Object.class);
	}

	/**
	 * 
	 * @param dataSeries
	 * @param frequency
	 * @param aggfun
	 * @param elemType
	 * @throws Exception
	 */
	public ResampledDataSeries(DataSeries<T> dataSeries, Frequency frequency, Function<List<T>, T> aggfun,
			Class<?> elemType) throws Exception {

		this(dataSeries, frequency, aggfun, 0, elemType);
	}

	/**
	 * 
	 * @param dataSeries
	 * @param frequency
	 * @param aggfun
	 * @param maxLen
	 * @param elemType
	 * @throws Exception
	 */
	public ResampledDataSeries(DataSeries<T> dataSeries, Frequency frequency, Function<List<T>, T> aggfun,
			int maxLen, Class<?> elemType) throws Exception {

		super(maxLen, elemType);
		__dsResamplerImpl = new DSResamplerImpl();
		__dsResamplerImpl.initDSResampler(dataSeries, frequency);
		__aggfun = aggfun;
	}

	/*
	 * (non-Javadoc)
	 * @see qy.jalgotrade.dataseries.resampled.DSResampler#buildGrouper(qy.jalgotrade.utils.resamplebase.TimeRange, java.lang.Object, qy.jalgotrade.bar.Bar.Frequency)
	 */
	@Override
	public Grouper<T> buildGrouper(TimeRange range, T value, Frequency frequency) {

		return __dsResamplerImpl.buildGrouper(range, value, frequency);
	}

	/*
	 * 
	 */
	@Override
	public void pushLast() throws Exception {

		__dsResamplerImpl.pushLast();
	}

	/*
	 * 
	 */
	@Override
	public void checkNow(ZonedDateTime dateTime) throws Exception {

		__dsResamplerImpl.checkNow(dateTime);
	}
}
